using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace NetCorePal.Extensions.CodeAnalysis.SourceGenerators;

[Generator]
public class CommandHandlerEntityMethodMetadataGenerator : IIncrementalGenerator
{
    public void Initialize(IncrementalGeneratorInitializationContext context)
    {
        var typeDeclarations = context.SyntaxProvider
            .CreateSyntaxProvider(
                predicate: (node, _) => node is ClassDeclarationSyntax || node is RecordDeclarationSyntax,
                transform: (ctx, _) => ctx.Node)
            .Where(n => n is ClassDeclarationSyntax || n is RecordDeclarationSyntax);

        var compilationAndTypes = context.CompilationProvider.Combine(typeDeclarations.Collect());

        context.RegisterSourceOutput(compilationAndTypes, (spc, source) =>
        {
            var (compilation, typeNodes) =
                ((Compilation, System.Collections.Immutable.ImmutableArray<SyntaxNode>))source;
            var handlerMetas =
                new List<(string HandlerType, string CommandType, string EntityType, string EntityMethodName)>();

            foreach (var typeDecl in typeNodes)
            {
                var semanticModel = compilation.GetSemanticModel(typeDecl.SyntaxTree);
                var symbol = semanticModel.GetDeclaredSymbol(typeDecl) as INamedTypeSymbol;
                if (symbol == null) continue;
                // 只处理实现 ICommandHandler<TCommand> 的类型
                if (!symbol.IsCommandHandler()) continue;
                // 获取命令类型
                var commandTypeSymbol = symbol.GetCommandFromCommandHandler();
                if (commandTypeSymbol == null) continue;
                var commandType = commandTypeSymbol.ToDisplayString();
                var handlerType = symbol.ToDisplayString();
                var handlerMethods = symbol.GetMembers().OfType<IMethodSymbol>();

                foreach (var method in handlerMethods)
                {
                    var syntaxRefs = method.DeclaringSyntaxReferences;
                    foreach (var syntaxRef in syntaxRefs)
                    {
                        var methodSyntax = syntaxRef.GetSyntax() as MethodDeclarationSyntax;
                        if (methodSyntax?.Body == null) continue;
                        // 记录调用的 entity 实例/静态方法
                        foreach (var invocation in methodSyntax.Body.DescendantNodes()
                                     .OfType<InvocationExpressionSyntax>())
                        {
                            if (invocation.IsEntityMethodInvocation(semanticModel, out var entityResult))
                            {
                                if (!string.IsNullOrEmpty(entityResult.entityType))
                                {
                                    handlerMetas.Add((handlerType, commandType, entityResult.entityType!,
                                        entityResult.methodName ?? string.Empty));
                                }
                            }
                        }

                        // 记录调用的 entity 构造函数
                        foreach (var objCreation in methodSyntax.Body.DescendantNodes()
                                     .OfType<ObjectCreationExpressionSyntax>())
                        {
                            var typeInfo = semanticModel.GetTypeInfo(objCreation).Type as INamedTypeSymbol;
                            if (typeInfo != null && typeInfo.IsEntity())
                            {
                                // 统一用 .ctor 作为构造方法名
                                handlerMetas.Add((handlerType, commandType, typeInfo.ToDisplayString(), ".ctor"));
                            }
                        }
                    }
                }
            }

            if (handlerMetas.Count > 0)
            {
                var sb = new StringBuilder();
                sb.AppendLine(
                    "// <auto-generated/>\nusing System;\nusing NetCorePal.Extensions.CodeAnalysis.Attributes;");
                foreach (var (handlerType, commandType, entityType, entityMethodName) in handlerMetas)
                {
                    sb.AppendLine(
                        $"[assembly: CommandHandlerEntityMethodMetadataAttribute(\"{handlerType}\", \"{commandType}\", \"{entityType}\", \"{entityMethodName}\")]\n");
                }

                spc.AddSource("CommandHandlerEntityMethodMetadata.g.cs", sb.ToString());
            }
        });
    }
}